#!/bin/bash
# Copyright (c) 2020 maminjie <maminjie1@huawei.com>
# SPDX-License-Identifier: MulanPSL-2.0

# ~/.oecrc format:
#
# [gitee]
# token = xxxxxx
#
conf_file=~/.oecrc
if [ ! -f $conf_file ]; then
    echo "Please create ~/.oecrc file!"; exit
fi

# get_config_value(file, section, key)
get_config_value() {
    file=$1; section=$2; key=$3
    value=$(awk -F '=' '/\['$section'\]/{a=1} (a==1 && $1~/'$key'/){a=0;sub(/^[[:blank:]]*|[[:blank:]]*$/,"",$2);print $2}' $file)
    echo "$value"
}

access_token=$(get_config_value "$conf_file" "gitee" "token")
if [ -z "$access_token" ]; then
    echo "token is empty!"; exit
fi

gitee_owner=src-openeuler
gitee_id=""
gitee_pwd=""

readonly OEC_VERSION="0.1.3"

# color
readonly COLOR_NONE="\033[0m"
readonly COLOR_RED="\033[0;31m"
readonly COLOR_GREEN="\033[0;32m"
readonly COLOR_YELLOW="\033[0;33m"

# errno
readonly EFAIL=1
readonly EPERM=2
readonly ENOENT=3

###############################################################################
# gitee.sh
#
# Refers to https://gitee.com/api/v5/swagger
#
readonly GITEE_HEADER="Content-Type: application/json;charset=UTF-8"

__get_http_code() {
    local code=$(echo "$1" | awk -F 'ResponseCode:' '{print $2}')
    echo "$code"
}

# __check_http_result(result, respcode)
__check_http_result() {
    local rc=$(__get_http_code "$1")
    if [ "$rc" = "$2" ]; then
        return 0
    elif [ "$rc" = "401" ]; then
        echo -e "${COLOR_RED}$1${COLOR_NONE}"
        exit $EPERM
    elif [ "$rc" = "404" ]; then
        return $ENOENT
    fi

    return $EFAIL
}

# gitee_get(url, param)
gitee_get() {
    local result=$(curl -s -X GET -w ' ResponseCode:%{http_code}' --header "$GITEE_HEADER" "$1?$2")
    echo "$result"
}

# gitee_post(url, value)
gitee_post() {
    local result=$(curl -s -X POST -w ' ResponseCode:%{http_code}' --header "$GITEE_HEADER" "$1" -d "$2")
    echo "$result"
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/forks
# fork_repo(repo)
#   Fork the specified repo
# Returns:
#       0 - success
#       1 - fail
# Example:
#   fork_repo fancytool
fork_repo() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/forks"
    value="{\"access_token\":\"$access_token\"}"
    result=$(gitee_post "$url" "$value")
    code=$(__get_http_code "$result")
    __check_http_result "$result" "201"
    ret=$?
    if [ $ret != 0 ]; then 
        echo -e "${COLOR_RED}$result${COLOR_NONE}"
        return 1
    fi
    return 0
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/pulls
# create_pr(repo, title)
#   Create Pull Request
# Returns:
#       0 - success
#       1 - fail
# Example:
#   create_pr fancytool "update scripts"
create_pr() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/pulls"
    value="{\"access_token\":\"$access_token\",\"title\":\"$2\",\"head\":\"$gitee_id:master\",\"base\":\"master\"}"
    # Prevent spaces in value, so add quotes
    result=$(gitee_post "$url" "$value")
    __check_http_result "$result" "201"
    ret=$?
    if [ $ret != 0 ]; then
        return 1
    fi
    return 0
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/pulls/{number}/comments
# check_pr_compile(repo, number)
#   Determine whether the Pull Request has been compiled successfully
# Returns:
#   0 - success
#   1 - fail
#   2 - warning
#   9 - error
# Example
#   check_pr_compile fancytool 1
check_pr_compile() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/pulls/$2/comments"
    param="access_token=$access_token&page=1&per_page=100"
    result=$(gitee_get "$url" "$param")
    __check_http_result "$result" "200"
    ret=$?
    if [ $ret != 0 ]; then
        echo -e "${COLOR_RED}$result${COLOR_NONE}"
        return 9
    fi
    result=$(echo "$result" | grep -oE '"body":"[^"]*"' | sed 's/\s//g')
    ret=1
    for i in $result; do
        success=$(echo $i | grep -oE 'SUCCESS')
        fail=$(echo $i | grep -oE '(FAILURE|FAILED)')
        warning=$(echo $i | grep -oE 'WARNING')
        if [ -z "$success" ] || [ -n "$fail" ]; then
            continue
        fi
        if [ -z "$warning" ]; then
            return 0
        else
            ret=2
        fi
    done
    return $ret
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/pulls/{number}/comments
# set_pr_comment(repo, number, comments)
#   Submit Pull Request comments
# Returns:
#   0 - success
#   1 - fail
# Example
#   set_pr_comment fancytool 1 "OK"
set_pr_comment() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/pulls/$2/comments"
    value="{\"access_token\":\"$access_token\",\"body\":\"$3\"}"
    result=$(gitee_post "$url" "$value")
    __check_http_result "$result" "201"
    ret=$?
    if [ $ret != 0 ]; then
        echo -e "${COLOR_RED}$result${COLOR_NONE}"
        return 1
    fi
    return 0
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/pulls/{number}/merge
# check_pr_merge(repo, number)
#   Determine whether the Pull Request has been merged
# Returns:
#       0 - merged
#       1 - opened
#       9 - error
# Example:
#   result=$(check_pr_merge fancytool 1)
check_pr_merge() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/pulls/$2/merge"
    param="access_token=$access_token"
    result=$(gitee_get "$url" "$param")
    __check_http_result "$result" "200"
    ret=$?
    if [ $ret != 0 ]; then
        echo -e "${COLOR_RED}$result${COLOR_NONE}"
        if [ $ret == $ENOENT ]; then
            return 9
        else
            return 1
        fi
    fi
    return 0
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/issues
# get_issues(repo, state)
#   Get the all issues of package
# Parameters:
#   state: open/progressing/closed/rejected/all
get_issues() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/issues"
    param="state=$2&sort=created&direction=desc&page=1&per_page=100"
    result=$(gitee_get "$url" "$param")
    echo "$result"
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/issues/{number}/comments
# get_issue_comment(repo, number)
get_issue_comment() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/issues/$2/comments"
    param="access_token=$access_token&page=1&per_page=100&order=asc"
    result=$(gitee_get "$url" "$param")
    echo "$result"
}

# https://gitee.com/api/v5/repos/{owner}/{repo}/issues/{number}/comments
# set_issue_comment(repo, number, comment)
# Returns:
#   0 - success
#   1 - fail
set_issue_comment() {
    url="https://gitee.com/api/v5/repos/$gitee_owner/$1/issues/$2/comments"
    value="{\"access_token\":\"$access_token\",\"body\":\"$3\"}"
    result=$(gitee_post "$url" "$value")
    __check_http_result "$result" "201"
    ret=$?
    if [ $ret != 0 ]; then
        return 1
    fi
    return 0
}

# check_personal_repo(repo)
check_personal_repo() {
    git_url="https://gitee.com/$gitee_id/$1"
    result=$(expect -c "
    spawn git ls-remote "$git_url"
    expect {
    \"*Username*\" {send "*"}
    \"*Password*\" {send "*"}
    }")
    result=$(echo "$result" | grep "Username")
    if [ -n "$result" ]; then
        return 1
    fi
    return 0
}

###############################################################################

get_list() {
    local lst=""

    if [ -f "$1" ]; then
        lst=$(cat $1)
    else
        lst=$1
    fi

    echo $lst
}


# fun pr_list
do_check_pr_merge() {  
    if [ $# != 1 ]; then
        usage_checkmerge; exit
    fi
    local pr_open_file=pr_open.txt
    local pr_merge_file=pr_merge.txt
    local pr_list=$(get_list "$1")

    rm -f $pr_merge_file $pr_open_file
    for pr in $pr_list; do
        printf "checking merge $pr "
        repo_pn=$(echo $pr | sed -e "s|.*$gitee_owner/||g" -e 's|/pulls/| |g')
        check_pr_merge $repo_pn
        ret=$?
        if [ $ret == 0 ]; then
            printf "(${COLOR_GREEN}merged${COLOR_NONE})\n"
            echo "$pr" >> $pr_merge_file
        elif [ $ret == 1 ]; then
            printf "(${COLOR_RED}opened${COLOR_NONE})\n"
            echo "$pr" >> $pr_open_file
        fi
    done
    echo "========================================="
    echo "Please check $pr_open_file and $pr_merge_file"
    echo "========================================="
}

# fun pr_list
do_check_pr_compile() {
    if [ $# != 1 ]; then
        usage_checkcompile; exit
    fi
    local pr_fail_file=pr_fail.txt
    local pr_succ_file=pr_succ.txt
    local pr_list=$(get_list "$1")

    rm -f $pr_fail_file $pr_succ_file
    for pr in $pr_list; do
        printf "checking compile $pr "
        repo_pn=$(echo $pr | sed -e "s|.*$gitee_owner/||g" -e 's|/pulls/| |g')
        check_pr_compile $repo_pn
        ret=$?
        if [ $ret == 0 ]; then
            printf "(${COLOR_GREEN}success${COLOR_NONE})\n"
            echo "$pr" >> $pr_succ_file
        elif [ $ret == 1 ]; then
            printf "(${COLOR_RED}fail${COLOR_NONE})\n"
            echo "$pr" >> $pr_fail_file
        elif [ $ret == 2 ]; then
            printf "(${COLOR_YELLOW}warning${COLOR_NONE})\n"
            echo "$pr" >> $pr_fail_file
        fi
    done
    echo "========================================"
    echo "Please check $pr_fail_file and $pr_succ_file"
    echo "========================================"
}

# fun pr_list comments
do_set_pr_comment() {
    if [ $# != 2 ]; then
        usage_setprcomment; exit
    fi
    local pr_list=$(get_list "$1")

    for pr in $pr_list; do
        printf "comment $pr \"$2\" "
        repo_pn=$(echo $pr | sed -e "s|.*$gitee_owner/||g" -e 's|/pulls/| |g')
        set_pr_comment $repo_pn "$2"
        ret=$?
        if [ $ret == 0 ]; then
            printf "(${COLOR_GREEN}success${COLOR_NONE})\n"
        fi
    done
}

read_account() {
    read -p "Username for GITEE: " gitee_id
    if [ -z $gitee_id ]; then
        echo "gitee id invalid"
        exit 1
    fi
    stty -echo
    read -p "Password for GITEE: " gitee_pwd
    stty echo
    if [ -z $gitee_pwd ]; then
        echo "gitee password invalid"
        exit 2
    fi
    echo ""
}

# func src_dir dst_dir repo
push_code() {
    local src_dir=$1
    local dst_dir=$2
    local repo=$3
    local srcpath=$src_dir/$repo
    local dstpath=$dst_dir/$repo

    # clone
    if [ ! -d $dstpath ]; then
        pushd $dst_dir
        git clone https://gitee.com/$gitee_id/$repo.git
        popd
    fi

    if [ ! -d $srcpath ] || [ ! -d $dstpath ]; then
        exit
    fi

    cp $srcpath/* $dstpath
    pushd $dstpath
    git add .
    git commit -m "package init"
    expect -c "
    spawn git push origin master
    expect {
    \"*Username*\" {send \"${gitee_id}\r\";exp_continue}
    \"*Password*\" {send \"${gitee_pwd}\r\";exp_continue}
    }
    "
    popd
}

# func src_dir dst_dir repo_list
do_submit_code() {
    if [ $# != 3 ]; then
        usage_submitcode; exit
    fi
    local src_dir=$1
    local dst_dir=$2
    local repo_list=$(get_list "$3")

    if [ -n "$repo_list" ]; then
        read_account
    fi

    for pkg in $repo_list; do
        check_personal_repo "$pkg"
        if [ $? != 0 ]; then
            echo -n "forking $pkg: "
            fork_repo $pkg
            if [ $? != 0 ]; then
                continue
            else
                echo -e "${COLOR_GREEN}success${COLOR_NONE}"
            fi
        fi
        sleep 2
        push_code $src_dir $dst_dir $pkg
        create_pr $pkg "package init"
        sleep 1
    done
}


__check_issue_result() {
    local result=$1
    if [ -z "$result" ]; then
        return 1
    fi
    local code=$(__get_http_code "$result")
    if [ "$code" = "401" ]; then
        echo -e "${COLOR_RED}$result${COLOR_NONE}"
        exit
    elif [ "$code" = "200" ]; then
        local empty=$(echo "$result" | grep -oE '^\[\]')
        if [ -n "$empty" ]; then
            return 1
        else
            return 0
        fi
    fi
    return 1
}

do_get_issues() {
    if [ $# != 2 ]; then
        usage_getissues; exit
    fi
    local pkgs=$(get_list "$2")

    printf "%s|%s|%s|%s|%s|%s|%s\n" package number title issue_type html_url repository user_name
    for pkg in $pkgs; do
        result=$(get_issues "$pkg" "$1")
        __check_issue_result "$result"
        if [ $? != 0 ]; then
            continue
        fi
        echo "$result" | awk -v repo=$pkg -F '"security_hole":[^}]*},' '{
            for (i=1;i<=NF;i++) {
                printf("%s|", repo);
                match($i, /"number":"([^"]*)"/,a);printf("%s|", a[1]);
                match($i, /"title":"([^"]*)"/,a);printf("%s|", a[1]);
                match($i, /"issue_type":"([^"]*)"/,a);printf("%s|", a[1]);
                match($i, /"html_url":"([^"]*)"/,a);printf("%s|", a[1]);
                match($i, /"full_name":"([^"]*)"/,a);printf("%s|", a[1]);
                match($i, /"name":"([^"]*)"/,a);printf("%s\n", a[1]);
            }
        }'
    done
}

do_get_issue_list() {
    if [ $# != 2 ]; then
        usage_getissuelist; exit
    fi
    local pkgs=$(get_list "$2")

    for pkg in $pkgs; do
        result=$(get_issues "$pkg" "$1")
        __check_issue_result "$result"
        if [ $? != 0 ]; then
            continue
        fi
        echo "$result" | awk -v repo=$pkg -F '"security_hole":[^}]*},' '{
            for (i=1;i<=NF;i++) {
                match($i, /"html_url":"([^"]*)"/,a);printf("%s\n", a[1]);
            }
        }'
    done
}

do_get_issue_comment() {
    if [ $# != 2 ]; then
        usage_getissuecomment; exit
    fi
    local pkg=$1
    local numbers=$(get_list "$2")

    printf "%s|%s|%s|%s\n" number title author comment
    for number in $numbers; do
        result=$(get_issue_comment "$pkg" "$number")
        __check_issue_result "$result"
        if [ $? != 0 ]; then
            continue
        fi
        echo "$result" | awk -F '"updated_at":[^}]*},' '{
            for (i=1;i<=NF;i++) {
                match($i, /"name":"([^"]*)"/,a);
                if (a[1] == "openeuler-ci-bot") {
                    continue;
                } else {
                    name=a[1];
                }
                match($i, /"number":"([^"]*)"/,a);printf("%s|", a[1]);
                match($i, /"title":"([^"]*)"/,a);printf("%s|", a[1]);
                printf("%s|", name);
                match($i, /"body":"([^"]*)"/,a);printf("%s\n", a[1]);
            }
        }'
    done
}

do_set_issue_comment() {
    local issueurls=""
    local repo=""
    local issuenums=""
    local comments=""
    if [ $# == 2 ]; then
        issueurls=$(get_list "$1")
        comments=$2
    elif [ $# == 3 ]; then
        repo=$1
        issuenums=$(get_list "$2")
        comments=$3
    else
        usage_setissuecomment; exit
    fi

    if [ -n "$issueurls" ]; then
        for url in $issueurls; do
            repo_inum=$(echo $url | sed -e "s|.*$gitee_owner/||g" -e 's|/issues/| |g')
            set_issue_comment $repo_inum "$comments"
        done
    else
        for num in $issuenums; do
            set_issue_comment "$repo" "$num" "$comments"
        done
    fi
}

usage() {
    echo "Usage: oec [GLOBALOPTS] SUBCOMMAND [OPTS] [ARGS...]"
    echo "or: oec help SUBCOMMAND"
    echo ""
    echo "openEuler command-line tools."
    echo "Type 'oec help <subcommand>' for help on a specific subcommand."
    echo ""
    echo "Commands:"
    echo "      submitcode (sc)"
    echo "                          Auto submit code. Including fork, clone, push, pr."
    echo "      checkcompile (cc, cprc)"
    echo "                          Check the Pull Request compile result"
    echo "      checkmerge (cm, cprm)"
    echo "                          Check the Pull Request merge result"
    echo "      setprcomment (sprc)"
    echo "                          Set the Pull Request comments"
    echo "      getissues (gi)      Get the issues of repos"
    echo "      getissuelist (gil)"
    echo "                          Get the issue url list of repos"
    echo "      getissuecomment (gic)"
    echo "                          Get the issue comments of repo"
    echo "      setissuecomment (sic)"
    echo "                          Set the issue comments of repo"
    echo "      help (?, h)         Give detailed help on a specific sub-command"
    echo ""
    echo "Global Options:"
    echo "      -v, --version       Show program's version number and exit"
    echo "      -h, --help          Show this help message and exit"
    echo ""
}

usage_submitcode() {
    echo "submitcode (sc): Auto submit code. Including fork, clone, push, pr."
    echo ""
    echo "Usage:"
    echo "      oec sc SOURCEDIR TARGETDIR PKGLIST"
    echo "      oec sc SOURCEDIR TARGETDIR LISTFILE"
    echo ""
    echo "Args:"
    echo "      SOURCEDIR           The source directory that contains specific packages"
    echo "      TARGETDIR           The target directory, where the packages are generally cloned from gitrepo"
    echo "      PKGLIST             The package name list, like: \"a b c d\""
    echo "      LISTFILE            The file that contains package name list"
    echo "                          Where each name is separated by a space or newline"
    echo ""
    echo "Examples:"
    echo "      oec sc /home/mine/src /home/mine/dst \"a b\""
    echo "                          Submit the two packages a and b to gitee."
    echo "                          sourcefiles in /home/mine/src and repos are cloned in /home/mine/dst"
    echo ""
}

usage_checkcompile() {
    echo "checkcompile (cc, cprc): Check the Pull Request compile result"
    echo ""
    echo "Usage:"
    echo "      oec cc PRLIST"
    echo "      oec cc LISTFILE"
    echo ""
    echo "Args:"
    echo "      PRLIST              The PR list"
    echo "                          like: \"https://gitee.com/owner/repo1/pulls/1 https://gitee.com/owner/repo2/pulls/2\""
    echo "      LISTFILE            The file that contains PR list"
    echo "                          Where each PR is separated by a space or newline"
    echo ""
    echo "Examples:"
    echo "      oec cc \"https://gitee.com/$gitee_owner/repo1/pulls/1\""
    echo ""
}

usage_checkmerge() {
    echo "checkmerge (cm, cprm): Check the Pull Request merge result"
    echo ""
    echo "Usage:"
    echo "      oec cm PRLIST"
    echo "      oec cm LISTFILE"
    echo ""
    echo "Args:"
    echo "      PRLIST              The PR list"
    echo "                          like: \"https://gitee.com/owner/repo1/pulls/1 https://gitee.com/owner/repo2/pulls/2\""
    echo "      LISTFILE            The file that contains PR list"
    echo "                          Where each PR is separated by a space or newline"
    echo ""
    echo "Examples:"
    echo "      oec cm \"https://gitee.com/$gitee_owner/repo1/pulls/1\""
    echo ""
}

usage_setprcomment() {
    echo "setprcomment (sprc): Set the Pull Request comments"
    echo ""
    echo "Usage:"
    echo "      oec sprc PRLIST COMMENTS"
    echo "      oec sprc LISTFILE COMMENTS"
    echo ""
    echo "Args:"
    echo "      PRLIST              The PR list"
    echo "                          like: \"https://gitee.com/owner/repo1/pulls/1 https://gitee.com/owner/repo2/pulls/2\""
    echo "      LISTFILE            The file that contains PR list"
    echo "                          Where each PR is separated by a space or newline"
    echo ""
    echo "Examples:"
    echo "      oec sprc \"https://gitee.com/$gitee_owner/repo1/pulls/1\" \"/retest\""
    echo ""
}

usage_getissues() {
    echo "getissues (gi): Get the issues of repos"
    echo ""
    echo "Usage:"
    echo "      oec gi state PKGLIST|LISTFILE"
    echo ""
    echo "Parameters:"
    echo "      state: open/progressing/closed/rejected/all"
    echo ""
}

usage_getissuelist() {
    echo "getissuelist (gil): Get the issue url list of repos"
    echo ""
    echo "Usage:"
    echo "      oec gil state PKGLIST|LISTFILE"
    echo ""
    echo "Parameters:"
    echo "      state: open/progressing/closed/rejected/all"
    echo ""
}

usage_getissuecomment() {
    echo "getissuecomment (gic): Get the issue comments of repo"
    echo ""
    echo "Usage:"
    echo "      oec gic PACKAGE ISSUENUMBERLIST|LISTFILE"
    echo ""
}

usage_setissuecomment() {
    echo "setissuecomment (sic): Set the issue comments of repo"
    echo ""
    echo "Usage:"
    echo "      oec sic ISSUEURLLIST|LISTFILE COMMENTS"
    echo "      oec sic PACKAGE ISSUENUMBERLIST|LISTFILE COMMENTS"
    echo ""
    echo "Parameters:"
    echo "      ISSUEURLLIST: issue url list, like: \"https://gitee.com/$gitee_owner/repo/issues/I1TUEU\""
    echo "      ISSUENUMBERLIST: issue number list, like: \"I1TUCX I1TTTV\""
    echo ""
}

do_help() {
    case $1 in
        "submitcode"|"sc")
            usage_submitcode
            ;;
        "checkcompile"|"cc"|"cprm")
            usage_checkcompile
            ;;
        "checkmerge"|"cm"|"cprm")
            usage_checkmerge
            ;;
        "setprcomment"|"sprc")
            usage_setprcomment
            ;;
        "getissues"|"gi")
            usage_getissues
            ;;
        "getissuelist"|"gil")
            usage_getissuelist
            ;;
        "getissuecomment"|"gic")
            usage_getissuecomment
            ;;
        "setissuecomment"|"sic")
            usage_setissuecomment
            ;;
        *)
            usage
            ;;
    esac
}

main() {
    local subcmd=$1

    if [ $# -gt 0 ]; then
        shift
    fi

    case $subcmd in
        "submitcode"|"sc")
            do_submit_code "$@"
            ;;
        "checkcompile"|"cc"|"cprc")
            do_check_pr_compile "$@"
            ;;
        "checkmerge"|"cm"|"cprm")
            do_check_pr_merge "$@"
            ;;
        "setprcomment"|"sprc")
            do_set_pr_comment "$@"
            ;;
        "getissues"|"gi")
            do_get_issues "$@"
            ;;
        "getissuelist"|"gil")
            do_get_issue_list "$@"
            ;;
        "getissuecomment"|"gic")
            do_get_issue_comment "$@"
            ;;
        "setissuecomment"|"sic")
            do_set_issue_comment "$@"
            ;;
        "help"|"?"|"h")
            do_help "$@"
            ;;
        "-v"|"--version")
            echo "$OEC_VERSION"
            ;;
        *)
            usage
            ;;
    esac
}

main "$@"

